package parser;

import parser.interfaces.Abstraction;
import parser.interfaces.Application;
import parser.interfaces.Identifier;

import java.util.ArrayList;

/**
 * @author IDo!
 * @version 1.0
 */
public class AST implements Application, Identifier, Abstraction {
    //
    //data part
    public String id;
    public StringBuilder param;
    public AST body, left, right;
    public Node type;
    public int value;

    public enum Node {
        Application, Identifier, Abstraction
    }

    /**
     * 将AST转为lambda表达式
     */
    public String finalToString(ArrayList<String> ctx){
        if(this.type==Node.Application)return this.ApptoString(ctx);
        else if(this.type==Node.Abstraction) return this.AbstoString(ctx);
        else return this.IdetoString(ctx);
    }

    public String ApptoString(ArrayList<String> ctx) {
        if(this.left.value==-1&&this.right.value==-1)
            return this.left.IdetoString(ctx)+" "+this.right.IdetoString(ctx);
        String leftstr = "", rightstr = "";
        switch (this.left.type) {
            case Application:
                leftstr = this.left.ApptoString(ctx);
                break;
            case Identifier:
                leftstr = this.left.IdetoString(ctx);
                break;
            case Abstraction:
                leftstr = this.left.AbstoString(ctx);
                break;
        }
        switch (this.right.type) {
            case Application:
                rightstr = this.right.ApptoString(ctx);
                break;
            case Identifier:
                rightstr = this.right.IdetoString(ctx);
                break;
            case Abstraction:
                rightstr = this.right.AbstoString(ctx);
                break;
        }
        return "("+leftstr + " " + rightstr+")";
    }

    public String IdetoString(ArrayList<String> ctx) {
        if (this.value == -1) return id;
        return ctx.get(this.value);
    }

    public String AbstoString(ArrayList<String> ctx) {
        if(this.type==Node.Abstraction&&this.param==null)return "";
        StringBuilder temp = new StringBuilder("\\" + this.param.toString() + ".");
        ArrayList<String> tmp = new ArrayList<String>();
        switch (this.body.type) {
            case Application:
                tmp.add(this.param.toString());
                tmp.addAll(ctx);
                temp.append(this.body.ApptoString(tmp));
                break;
            case Identifier:
                tmp.add(this.param.toString());
                tmp.addAll(ctx);
                temp.append(this.body.IdetoString(tmp));
                break;
            case Abstraction:
                tmp.add(this.param.toString());
                tmp.addAll(ctx);
                temp.append(this.body.AbstoString(tmp));
                break;
        }
        return temp.toString();
    }


    /**
     * abstraction、application和identifier结点的构造方法
     */
    public AST(StringBuilder id, AST next) {
        this.param = id;
        this.body = next;
        this.type = Node.Abstraction;
    }

    public AST(AST left, AST right) {
        this.left = left;
        this.right = right;
        this.type = Node.Application;
    }

    public AST(int value) {
        this.value = value;
        this.type = Node.Identifier;
    }

    /**
     *  构造自由变量节点
     */
    public AST(int value, String id) {
        this.value = value;
        this.id = id;
        this.type = Node.Identifier;
    }

    @Override
    public String toString() {
        return "AST{" +
                "id='" + id + '\'' +
                ", param=" + param +
                ", type=" + type +
                ", value=" + value +
                ", body=" + body +
                ", \n\tleft=" + left +
                ", \n\tright=" + right +

                '}';
    }
}

//    AST{type=Application,
//
//        left=AST {
//                  param=n, type=Abstraction,
//                  body=AST{
//                           param=f, type=Abstraction,
//                           body=AST{
//                                    param=x, type=Abstraction,
//                                    body=AST{
//                                             type=Application,
//                                             left=AST{
//                                                      type=Identifier, value=1
//                                             },
//                                             right=AST{
//                                                       type=Application,
//                                                       left=AST{
//                                                                type=Application,
//                                                                left=AST{
//                                                                         type=Identifier, value=2,
//                                                                         }
//                                                                right=AST{
//                                                                          type=Identifier, value=1
//                                                                         }
//                                                                },
//                                                      right=AST{
//                                                                type=Identifier, value=0,
//                                                                }
//                                                      }
//                                             },
//                                      },
//                          },
//                  },
//
//
//        right=AST{param=f, type=Abstraction,
//                  body=AST{param=x, type=Abstraction
//                           body=AST{type=Identifier, value=0,
//                                    },
//                          },
//                  }
//    }
